package org.boot.thread;

import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

import static org.boot.thread.constant.ThreadConstant.*;

/**
 * 异步操作相关测试
 *
 * @author LiKun
 * @date 2021/11/24 20:02
 */
public class AsyncOperate {

    private final Logger logger = LoggerFactory.getLogger(AsyncOperate.class);
    // 默认的线程池
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();

    @Before
    public void init() {
        // 缓冲队列(执行缓冲任务的队列)
        this.threadPoolTaskExecutor.setQueueCapacity(0);
        // 最大线程数
        this.threadPoolTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        // 核心线程数
        this.threadPoolTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        // 线程空闲时间(超时后将销毁非核线程)
        this.threadPoolTaskExecutor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
        // 线程拒绝策略(重试添加当前的任务直到成功)
        this.threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 线程名称前缀
        this.threadPoolTaskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);
        // 初始化线程池
        this.threadPoolTaskExecutor.initialize();
    }

    /**
     * 验证 {@link ExecutorService#submit(Callable)} 方法的使用
     *
     * @throws Exception 异常信息
     */
    @Test
    public void verifyExecutorServiceSubmit() {
        final int times = 25;
        final List<Future<LocalDateTime>> futures = new ArrayList<>();

        final long millis = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            futures.add(Executors.newFixedThreadPool(times).submit(() -> {
                try {
                    final long time = ThreadLocalRandom.current().nextLong(10000);
                    System.out.println("随机数为: " + time);
                    Thread.sleep(time);
                } catch (InterruptedException exception) {
                    System.out.println("线程休眠出现异常");
                }
                return LocalDateTime.now();
            }));
        }
        System.out.println("总耗时: " + (System.currentTimeMillis() - millis));
    }

    /**
     * 验证 {@link CountDownLatch} 的线程同步功能
     *
     * @throws Exception 异常信息
     */
    @Test
    public void verityCountDownLatch() throws Exception {
        final int times = 12; // 注：如果阻塞队列 == 0，则改值必须小于 (最大线程数 + 1)；如果缓冲队列 ！= 0，则该值必须小于等于 核心线程数
        final List<Future<LocalDateTime>> futures = new ArrayList<>();
        final CountDownLatch countDownLatch = new CountDownLatch(times);

        // 动态设置核心线程数，以解决线程同步可能的阻塞问题（不推荐，有Bug-其他地方可能会用到该线程池-占用线程池中的线程）
//        threadPoolTaskExecutor.setCorePoolSize(times + 1);
        final long millis = System.currentTimeMillis();

        for (int i = 0; i < times; i++) {
            // 等价于 Executors.newFixedThreadPool(times).submit() 方法
            futures.add(CompletableFuture.supplyAsync(() -> {
                final long time = ThreadLocalRandom.current().nextLong(5000);
                logger.debug("随机数为: " + time);
                try {
                    Thread.sleep(time);

                    // 等待其他线程完成任务
                    countDownLatch.countDown();
                    countDownLatch.await();

                    logger.debug(String.valueOf(System.currentTimeMillis())); // 输出时间以证明是否同步
                } catch (InterruptedException exception) {
                    logger.error("线程休眠被打断...");
                }
                return LocalDateTime.now();
            }, threadPoolTaskExecutor));
        }
        countDownLatch.await(); // 等待全部的异步线程执行完成
        logger.info("总耗时: " + (System.currentTimeMillis() - millis));
    }

    // https://blog.csdn.net/qq_31865983/article/details/106137777

}
